def is_hex_num(string):
    '''Check if a string is in hex'''
    if type(string)!=(type('1')):raise TypeError
    h='0123456789ABCDEF'
    for char in string:
        if not char in h:return False
    return True


class Command:
    '''The class Command provides a convenient way to deal with the Command code, including Cutting and Filling the Code
    Usage:
        c=Command('AE[..]0[.]02DF',['address','circuit'])

        para = c.extract('AE0A0102DF') 
        #para=['address=01','circuit=1']

        code = c.fill(['address=01','circuit=1'])
        #code = 'AE010102DF'
    '''    

    @classmethod
    def command_parser(cls,code):
        '''This function reads a 'AF[..]32[.]5'-like code and return:
        (1)Its length
        (2)The number of parameter([..])
        (3)A list of the length of each parameter, in order.

        If its not valid, raise RuntimeError
        '''
        code_length=len(code)-code.count('[')-code.count(']')
        para_length=[]
        para_position=[]
        length,statue,k=0,0,0
        position_index=0
        for c in code:
            if is_hex_num(c):
                if statue!=0: raise RuntimeError ("Wrong Command")
                position_index+=1
            elif c=='[':
                if statue!=0: raise RuntimeError ("Wrong Command")
                k+=1
                statue=1
                para_position.append(position_index)
            elif c=='.':
                if statue!=1: raise RuntimeError ("Wrong Command")
                length+=1
                position_index+=1
            elif c==']':
                if statue!=1: raise RuntimeError ("Wrong Command")
                para_length.append(length)
                statue,length=0,0
            else: raise RuntimeError("Wrong Command")

        return code_length,k,para_length,para_position

    def __init__(self,code,parameter=[]):
        self.code=code

        (self.length\
        ,self.parameter_number\
        ,self.parameter_length\
        ,self.parameter_position)=self.command_parser(code)
        
        self.parameter_name=parameter
        self.code=code.replace('[','').replace(']','')
        

    def extract(self,code):
        '''This method reads a real-world code and
        returns a list of parameters)'''
        
        if self.length!=len(code):raise RuntimeError("Wrong Command")

        i=0
        while i<self.length:
            if is_hex_num(self.code[i])and code[i]!=self.code[i]:
               raise RuntimeError("Wrong Command")
            i+=1;

        para_value=[]

        for i in range(self.parameter_number):
            left,right=(self.parameter_position[i],self.parameter_position[i]+self.parameter_length[i])
            value=code[left:right]
            para_value.append(self.parameter_name[i]+'='+value)

        return para_value
                
        
        

    def fill(self,parameter_value_pair=[]):
        '''This method uses parameter value list and
        generate a filled code'''
        
##        The parameter is like ['address=04','minute=02']
        result_code=self.code

        if parameter_value_pair==[]:return self.code

        for p in parameter_value_pair:
            l=p.split('=')
            para_key,para_value=l[0],l[1]
            if not para_key in self.parameter_name : continue # this parameter isn't useful in this case. 
            index=self.parameter_name.index(para_key)
            this_length=self.parameter_length[index]
            this_pos=self.parameter_position[index]

            left,right=this_pos,this_pos+this_length

# Check if the length of given parameter is same to the config length  
            if len(para_value)==this_length:
                result_code=result_code[:left]+para_value[:]+result_code[right:]
            elif len(para_value)<this_length:
                print 'Warning: ',self.code,' :parameter length and code length not match'
                dif=this_length-len(para_value)
                result_code=result_code[:left]+'0'*dif+para_value[:]+result_code[right:]

            elif len(para_value)>this_length:
                dif=len(para_value)-this_length
                if para_value[0:dif]=='0'*dif:
                    print 'Warning: ',self.code,' :parameter length and code length not match'
                    para_value=para_value[dif:]
                    result_code=result_code[:left]+para_value[:]+result_code[right:]
            else: raise ValueError("Parameter length and code length not match")

        if is_hex_num(result_code):
            return result_code
        else:
            raise ValueError("Parameter wrong")      

##        if len(action[k])==length:
##            ret_command+=action[k]
##        elif len(action[k])<length:
##            print (scenario_name,'Warning : Command length not match',action)
##            dif=length-len(action[k])
##            read_action+='0'*dif+action[k]      
##        elif len(action[k])>length:
##            dif=len(action[k])-length
##            if action[k][0:dif]=='0'*dif:
##                print  (scenario_name,'Warning : Command length not match',action)
##                read_action+=action[k][dif:]
##            else:
##                raise RuntimeError('Command length not match')
##        length=0
##        statue=None
##        action_list_filled.append(read_action)
##


if __name__=='__main__':
    print Command.command_parser('AF[..]00[.]1AB')
    c=Command('123[...]4[.]',['address','minute'])

    # print c.code
    # print 'para len=',c.parameter_length
    # print 'para_pos=',c.parameter_position
    # print 'para_name=',c.parameter_name
    # print c.extract('12355545')
    

    print c.fill(['address=044','minute=1'])
    print c.fill(['minute=1','address=044'])
    print c.fill(['address=4','minute=4'])
    print c.fill(['address=00004','minute=02'])

    c=Command('123123123')
    print c.extract('123123123')
    print c.fill(['address=1'])
